#include "realtimer.h"
#include "container.h"

namespace Cross
{

class SearchTree
{
public:
    ~SearchTree()
    {
        for(auto iter=_func.begin();iter!=_func.end();++iter)
        {
            delete *iter;
        }
        _func.clear();
        for(auto iter=_child.begin();iter!=_child.end();++iter)
        {
            delete iter->second;
        }
        _child.clear();
    }
    SearchTree* FindChild(uint16 key)
    {
        auto iter=_child.find(key);
        if(iter!=_child.end())
            return iter->second;
        return nullptr;
    }
    SearchTree* FindChildAdd(uint16 key)
    {
        auto iter=_child.find(key);
        if(iter!=_child.end())
            return iter->second;
        SearchTree* child=new SearchTree;
        _child.insert(key,child);
        return child;
    }
    void AddFunction(IRealTimerFunctor* func)
    {
        _func.push_back(func);
    }
    void DoEachTimerFunction(const DateTime& dateTime)
    {
        for(auto iter=_func.begin();iter!=_func.end();++iter)
        {
            (*iter)->Invoke(dateTime);
        }
    }
private:
    HashMap<uint16,SearchTree*> _child;
    STLVector<IRealTimerFunctor*>::type _func;
};

RealTimer::~RealTimer()
{
    delete _funcTree;
    _funcTree=nullptr;
}

RealTimer::RealTimer(const DateTime& now)
{
    _funcTree=new SearchTree;
    _lastDateTime=now;
}

void RealTimer::Tick(const DateTime& now)
{
    if(now.Minute!=_lastDateTime.Minute)
    {
        uint64 tickSecond=DateTimeToSecond(_lastDateTime);
        DateTime dateTime;
        do
        {
            tickSecond+=60;
            SecondToDateTime(tickSecond,dateTime);
            TickEveryMinute(dateTime);
        }while(dateTime.Minute!=now.Minute);
        _lastDateTime=now;
    }
}

void RealTimer::TickEveryMinute(const DateTime& now)
{
    SearchTree* hourTree=_funcTree->FindChild(now.Minute);
    if(!hourTree)
        return;
    hourTree->DoEachTimerFunction(now);
    SearchTree* dayTree=hourTree->FindChild(now.Hour);
    if(!dayTree)
        return;
    dayTree->DoEachTimerFunction(now);
    SearchTree* monthTree=dayTree->FindChild(now.Date);
    if(!monthTree)
        return;
    monthTree->DoEachTimerFunction(now);
    SearchTree* yearTree=monthTree->FindChild(now.Month);
    if(!yearTree)
        return;
    yearTree->DoEachTimerFunction(now);
}

void RealTimer::RegisterEveryHour(const DateTime& dateTime,IRealTimerFunctor* func)
{
    SearchTree* hourTree=_funcTree->FindChildAdd(dateTime.Minute);
    hourTree->AddFunction(func);
}

void RealTimer::RegisterEveryDay(const DateTime& dateTime,IRealTimerFunctor* func)
{
    SearchTree* hourTree=_funcTree->FindChildAdd(dateTime.Minute);
    SearchTree* dayTree=hourTree->FindChildAdd(dateTime.Hour);
    dayTree->AddFunction(func);
}

void RealTimer::RegisterEveryMonth(const DateTime& dateTime,IRealTimerFunctor* func)
{
    SearchTree* hourTree=_funcTree->FindChildAdd(dateTime.Minute);
    SearchTree* dayTree=hourTree->FindChildAdd(dateTime.Hour);
    SearchTree* monthTree=dayTree->FindChildAdd(dateTime.Date);
    monthTree->AddFunction(func);
}

void RealTimer::RegisterEveryYear(const DateTime& dateTime,IRealTimerFunctor* func)
{
    SearchTree* hourTree=_funcTree->FindChildAdd(dateTime.Minute);
    SearchTree* dayTree=hourTree->FindChildAdd(dateTime.Hour);
    SearchTree* monthTree=dayTree->FindChildAdd(dateTime.Date);
    SearchTree* yearTree=monthTree->FindChildAdd(dateTime.Month);
    yearTree->AddFunction(func);
}

class WeekTimerFunctor:public IRealTimerFunctor
{
public:
    ~WeekTimerFunctor()
    {
        delete _func;
        _func=nullptr;
    }
    WeekTimerFunctor(IRealTimerFunctor* func,uint16 weekDay)
    {
        _func=func;
        _weekDay=weekDay;
    }
    virtual void Invoke(const DateTime& now)
    {
        if(now.WeekDay==_weekDay)
        {
            _func->Invoke(now);
        }
    }
private:
    IRealTimerFunctor* _func;
    uint16 _weekDay;
};

void RealTimer::RegisterEveryWeek(const DateTime& dateTime,IRealTimerFunctor* func)
{
    RegisterEveryDay(dateTime,new WeekTimerFunctor(func,dateTime.WeekDay));
}

}
